﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Net.WebSockets;

namespace WebSocketClient
{
    class Program
    {
        static readonly string mask = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";  // 这是算法中要用到的固定字符串

        private static bool IsSendData;

        static void Main(string[] args)
        {
            ClientWebSocket webSocket = new ClientWebSocket();

            string ip = "127.0.0.1";
            int port = 8800;

            Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            client.Connect(new IPEndPoint(IPAddress.Parse(ip), port));

            Console.WriteLine("客户端启动！");



            string sesult_Data = string.Format(@"
                        GET /chat HTTP/1.1
                        Host: {0}:{1}  
                        Sec-WebSocket-Key: {2}        
                        ",
                        
                        ip, port, Convert.ToBase64String(Encoding.UTF8.GetBytes("hello"))
                        );
            Console.WriteLine("send: " + sesult_Data);
            client.Send(Encoding.UTF8.GetBytes(sesult_Data), SocketFlags.None);



            Thread t = new Thread(ReceiveExecute);
            t.IsBackground = true;
            t.Start(client);

            if (!IsSendData)
            {
                Thread.Sleep(10000);
                IsSendData = true;
                for (int i = 0; i < 10; i++)
                {
                    Thread.Sleep(100);
                    byte[] sendData = encodeDataFrame(DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss"));
                    client.Send(sendData, SocketFlags.None);
                    Console.WriteLine("Send: " + sendData);
                }
            }


            Console.ReadLine();
        }

        static void ReceiveExecute(Object obj)
        {
            Socket client = obj as Socket;
            byte[] buffer = new byte[1024];
            int count = client.Receive(buffer, SocketFlags.None);
            if (count > 0)
            {
                Console.WriteLine("receive: " + Encoding.UTF8.GetString(buffer, 0, count));

                //校验  Sec-WebSocket-Key  Sec-WebSocket-Accept

                while (true)
                {
                    buffer = new byte[1024];
                    count = client.Receive(buffer, SocketFlags.None);
                    if (count > 0)
                        decodeDataFrame(buffer, count, client);
                }
            }
        }


        //数据帧的解析代码：      
        static string decodeDataFrame(byte[] buffer, int count, Socket client)
        {
            int type = buffer[0] & 0xF;

            if (type == 0x1) //文本
            {
                int mask = buffer[1] & 0x80;
                int len = buffer[1] & 0x7f;
                int dataLen = 0;
                int index = 2;

                if (len < 126)
                {
                    dataLen = len;
                }
                else if (len == 126)
                {
                    //+2
                    ByteToshort(ref dataLen, buffer);
                    index += 2;
                }
                else
                {
                    //+4
                    ByteToint(ref dataLen, buffer);
                    index += 4;
                }
                byte[] mark_Key = new byte[4];
                if (mask == 0x80)
                {
                    mark_Key = new byte[] { buffer[index], buffer[index + 1], buffer[index + 2], buffer[index + 3] };
                    index += 4;
                }
                if (count >= dataLen + index)
                {
                    byte[] real_Data = buffer.ToList().Skip(index).ToList().ToArray();
                    if (mask == 0x80)
                    {
                        for (int i = 0; i < real_Data.Length; i++)
                        {
                            real_Data[i] = (byte)(mark_Key[i % 4] ^ real_Data[i]);
                        }
                    }

                    Console.WriteLine("receive All: " + Encoding.UTF8.GetString(real_Data, 0, dataLen));
                    //剩余数据     

                }
                else
                {
                    //超长字段  不足

                    //byte[] data_all = new byte[dataLen];
                    //byte[] temp = new byte[1024];
                    //count = client.Receive(temp, SocketFlags.None);

                    //if (count > 0)
                    //{
                    //    decodeDataFrame(temp, count, client);
                    //}
                }
            }

            return null;
        }

        static void ByteToshort(ref int len, byte[] result)
        {
            len += (int)((result[2] << 8));
            len += (int)((result[3]));
        }

        static void ByteToint(ref int len, byte[] result)
        {
            len += (int)((result[2] << 24));
            len += (int)((result[3] << 16));
            len += (int)((result[4] << 8));
            len += (int)((result[5]));
        }


        //数据帧的编码： 
        static byte[] encodeDataFrame(string msg)
        {
            byte[] data = Encoding.UTF8.GetBytes(msg);
            List<byte> list = new List<byte>();
            byte mask = 0x0;//0x80;   //加密
            list.Add(0x80 | 0x1);  // 0x80文本类型
            if (data.Length < 126)
            {
                list.Add((byte)(mask | data.Length));
            }
            else if (data.Length >= 126 && data.Length <= 65535) //2 20次方
            {
                list.Add((byte)(mask | 126));
                list.AddRange(shortToByte(data.Length));
            }
            else
            {
                list.Add((byte)(mask | 127));
                list.AddRange(intToByte(data.Length));
            }
            if (mask == 0x80)
            {
                byte[] mark_Key = new byte[] { 0x1, 0x2, 0x3, 0x4 };
                for (int i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)(mark_Key[i % 4] ^ data[i]);
                }

                list.AddRange(mark_Key);
            }

            list.AddRange(data);

            return list.ToArray();
        }

        static byte[] shortToByte(int len)
        {
            byte[] result = new byte[2];

            result[0] = (byte)((len >> 8) & 0xff);
            result[1] = (byte)((len) & 0xff);

            return result;
        }

        static byte[] intToByte(int len)
        {
            byte[] result = new byte[4];

            result[0] = (byte)((len >> 24) & 0xff);
            result[1] = (byte)((len >> 16) & 0xff);
            result[2] = (byte)((len >> 8) & 0xff);
            result[3] = (byte)((len) & 0xff);

            return result;
        }


       
        /// <summary>
        /// 解析客户端数据包
        /// </summary>
        /// <param name="recBytes">服务器接收的数据包</param>
        /// <param name="recByteLength">有效数据长度</param>
        /// <returns></returns>
        private static string AnalyticData(byte[] recBytes, int recByteLength)
        {
            if (recByteLength < 2) { return string.Empty; }

            bool fin = (recBytes[0] & 0x80) == 0x80; // 1bit，1表示最后一帧
            if (!fin)
            {
                return string.Empty;// 超过一帧暂不处理
            }

            bool mask_flag = (recBytes[1] & 0x80) == 0x80; // 是否包含掩码
            if (!mask_flag)
            {
                return string.Empty;// 不包含掩码的暂不处理
            }

            int payload_len = recBytes[1] & 0x7F; // 数据长度

            byte[] masks = new byte[4];
            byte[] payload_data;

            if (payload_len == 126)
            {
                Array.Copy(recBytes, 4, masks, 0, 4);
                payload_len = (UInt16)(recBytes[2] << 8 | recBytes[3]);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 8, payload_data, 0, payload_len);

            }
            else if (payload_len == 127)
            {
                Array.Copy(recBytes, 10, masks, 0, 4);
                byte[] uInt64Bytes = new byte[8];
                for (int i = 0; i < 8; i++)
                {
                    uInt64Bytes[i] = recBytes[9 - i];
                }
                UInt64 len = BitConverter.ToUInt64(uInt64Bytes, 0);

                payload_data = new byte[len];
                for (UInt64 i = 0; i < len; i++)
                {
                    payload_data[i] = recBytes[i + 14];
                }
            }
            else
            {
                Array.Copy(recBytes, 2, masks, 0, 4);
                payload_data = new byte[payload_len];
                Array.Copy(recBytes, 6, payload_data, 0, payload_len);

            }

            for (var i = 0; i < payload_len; i++)
            {
                payload_data[i] = (byte)(payload_data[i] ^ masks[i % 4]);
            }
            return Encoding.UTF8.GetString(payload_data);
        }


        /// <summary>
        /// 打包服务器数据
        /// </summary>
        /// <param name="message">数据</param>
        /// <returns>数据包</returns>
        private static byte[] PackData(string message)
        {
            byte[] contentBytes = null;
            byte[] temp = Encoding.UTF8.GetBytes(message);

            if (temp.Length < 126)
            {
                contentBytes = new byte[temp.Length + 2];
                contentBytes[0] = 0x81;
                contentBytes[1] = (byte)temp.Length;
                Array.Copy(temp, 0, contentBytes, 2, temp.Length);
            }
            else if (temp.Length < 0xFFFF)
            {
                contentBytes = new byte[temp.Length + 4];
                contentBytes[0] = 0x81;
                contentBytes[1] = 126;
                contentBytes[2] = (byte)(temp.Length & 0xFF);
                contentBytes[3] = (byte)(temp.Length >> 8 & 0xFF);
                Array.Copy(temp, 0, contentBytes, 4, temp.Length);
            }
            else
            {
                // 暂不处理超长内容
            }
            return contentBytes;
        }


    }

    /*   客户端请求
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
 */
}
